Skip to content

State Management Tools

(Optional lesson)

So here's one of the most common React questions I've been asked:

Should we use something like Redux to manage global state for us? Or is React capable enough on its own?

Frustratingly, I don't have a simple yes/no answer to these questions. The unfortunate answer is it depends!

In this lesson, we're going to dig into these questions. We'll explore some of the history around what Redux is, why it's been useful, and how things stand today. I'll also share the formula I use when deciding how I want to manage state in my applications.

Some background info

In the early days of React, it was expected that React would only be one piece of your front-end stack. The best practice was to combine React with something like Flux, Redux, or MobX.

Specifically, the idea was to use React for local state, and a tool like Redux for global state.

"Local state" is state that is only needed in one particular part of the application. Most of the examples we've seen in this module have been local state. Things like how many times you've clicked the toonie, or what the current value is of a controlled text input.

"Global state", on the other hand, is for broader things. For example, data about the currently-logged-in user. Or maybe the currently-selected color theme (dark/light). A single piece of global state might be used in a dozen different corners of the application.

(The line between these two categories can be blurry; it's more of a spectrum than a binary choice.)

The thinking was that React state was well-suited for small, simple pieces of local state, but it wasn't powerful or flexible enough for global state. It was too difficult to move a state variable across the application, and it was hard to orchestrate complex state changes.

From ~2014-2016, a number of packages were competing to be the "global state" partner for React. In the end, the undisputed champion was Redux.

Intro to Redux

It's difficult to describe Redux without going on a huge tangent, but I'll do my best to summarize it quickly.

In Redux, our global state is represented as a single object that floats outside our React application. This state can't be directly edited; instead, Redux listens for "actions", events that signify that something's happening in our application.

All of the actions are meticulously logged by Redux, and can almost be read like a story to understand what's going on in our application. For example, the log for an e-commerce app might look like this:

  • User logs in
  • User submits search form
  • Search results received from server
  • User clicks on "Page 2" of search results
  • Search results received from server
  • User adds "Hello Kitty Coffee Machine" item to cart

Each of these actions fires through Redux, and we can write some code that controls how these actions should affect the state. For example, here's how we might process that last "add item to cart" action:

// This isn't *exactly* how code looks in Redux,
// but the core idea is the same:
function addItemToCart(state, action) {
const nextState = [
// All of the current items in our cart...
...state,
// ...and this one new item:
{
id: action.item.id,
quantity: action.quantity,
}
];
return nextState;
}

Like in React, state updates in Redux are immutable. In fact, Redux state has a lot in common with React state! But Redux gives us additional super-powers.

The Redux devtools, for example, has a feature known as "time-travel debugging". We can step through Redux's meticulous log of actions and inspect the UI at each moment in time, rewinding and pausing. Redux is the ultimate detective tool when it comes to debugging.

And Redux is incredibly customizable. It offers a bunch of additional APIs, like middleware and store enhancers, to extend what Redux does out-of-the-box. As a result, there is a rich ecosystem of Redux add-ons, tools that make Redux even more powerful.

But all of this power and flexibility comes at a cost: there's a significant amount of friction when using Redux. Everything takes a bit longer and requires a bit more code, because you have to scaffold out a bunch of stuff. The #1 complaint about Redux is that there's "too much boilerplate".

It also has a pretty steep learning curve. Redux leans heavily on functional programming principles that take a while to wrap your mind around.

Relevance in modern React

So, it was considered a "best practice" a few years ago to use Redux to manage global state. But how do things stand today, in the era of modern hooks-based React? Is Redux still relevant?

This is a controversial question, and something that still gets debated a lot in the community. There isn't a widely-accepted answer.

Some say that Redux is as useful as ever; it provides a ton of sophisticated tooling and insights. They'll point to the NPM download numbers, which show that Redux is still growing in popularity:

Graph showing Redux downloads going from 1 million in 2018 to 9 million in 2023. The line is mostly linear but does curve upwards slightly.

On the other side, folks will point out that React has evolved quite a bit in the past few years. It's become much more capable on its own. It's even integrated some of Redux's features, like the useReducer hook (discussed in Module 5)! Modern React offers most of the benefits, and none of the drawbacks.

Interestingly, Redux was created by Dan Abramov and Andrew Clark. Both of them now work on the React core team, and have distanced themselves from Redux. Here's what Dan had to say about when he would choose to use Redux:

Here's what I think: Both groups make some good points, and I think they're both right in certain contexts. But it really depends on the type of application we're building.

My own biases

I'm going to elaborate on my viewpoint, but first I want to share a bit of my personal history with Redux.

I was an “early adopter” with Redux. I started using it in side projects when it was brand new, and I friggin’ loved it. Once I got the hang of it, it felt so elegant and delightful to me.

I worked with Redux at multiple jobs, including Breather, Unsplash, and Khan Academy. I've also built lots of side-projects using Redux.

For example: a few years ago, I created Beatmapper, a 3D level editor for the VR rhythm game “Beat Saber”:

This project uses Redux extensively, and I believe it helped quite a bit in terms of keeping the application maintainable!

That said, I have lots of other projects that don't use Redux. This course platform, for example, doesn't use Redux at all! Neither does my blog, joshwcomeau.com.

For me, whether or not to use Redux really depends on the type of application. A blog, a course platform, and a Beat Saber level editor are all very different types of projects, and different jobs require different tools.

Application types

Broadly speaking, I think all web applications can be grouped into 3 categories:

A 3-column diagram showing types of applications. Each category is discussed below

1. Not much state

This first category includes most things we'd call "websites". Static sites, news websites, and blogs all fit into this category.

To be clear, these applications might still have quite a bit of local state! My blog, for example, has some very dynamic widgets. But in terms of global state, things are fairly minimal.

2. Mostly client state

This category is mostly "things that used to be desktop applications". Photoshop, video editors, and word processors all fit into this category.

In this category, there's a lot of complex global state. We're doing a lot of state manipulation in the browser. We may still save the result "in the cloud" rather than on the user's device, but they're fundamentally client-heavy applications.

3. Mostly server state

In this final category, we have web applications that work primarily with server state.

I think the clearest example is an analytics dashboard:

a generic analytics dashboard, showing traffic analytics

We have a bunch of complex data in a database, and our app fetches that data and presents it to the user. These applications are essentially interfaces that let users read and manipulate data that lives in a database somewhere.

This is a broad category. Most apps fit into it, including most SaaS applications, social networks, search engines, e-commerce sites, etc.

Alright, now that we've defined these categories, let's talk about why they matter.

The right tool for the job

So, the reason I think it's valuable to split apps into these categories is that the challenges are different.

For the mostly-static websites in Category 1, we don't really have to worry when it comes to global state. There isn't much of it, and it tends to be (relatively) straightforward. My blog fits into this category, and while there are plenty of challenges, global state isn't one of them. I use React exclusively for all state, and it works great.

For the client-heavy applications in Category 2, Redux is awesome. I wouldn't go as far as to say that it's necessary—React really has become much more capable in the past few years—but it's still a very helpful tool! Redux helps keep the code simple even as the features become more and more complex. And it has a lot of built-in performance optimizations.

Things get interesting when we talk about the server-heavy applications in Category 3.

The biggest state-related challenges in this category are all network-related:

  • Fetching the right data at the right time.
  • Making sure the data doesn't grow stale by revalidating it (fetching it again), and customizing the revalidation for the specific situation.
  • Caching the data so that multiple components don't repeat requests unnecessarily.
  • Optimistic UI updates, so that we "predict" how network requests will resolve.
  • Pagination, requesting small slices of the data and letting the user pull new data as-needed.

And here's the thing: Redux doesn't really address any of them! Redux is a state-management tool, it doesn't help with network stuff.

Over the past few years, several tools have popped up which are purpose-built to help us manage these sorts of network-related challenges. These tools include Apollo, react-query, and SWR. We'll learn more about them in Module 3!

If you're building a video game, or an audio editor, Redux is great. But most of us aren't building those sorts of applications. I'd guess that 95% of React applications are either Category 1 or Category 3.

And so, this is the mental model I use when figuring out which tool to use:

  1. If it's a static website, like my blog, I use React alone (with the help of context, discussed in Module 4).
  2. If it's a client-heavy application like my Beat Saber level editor, I use Redux for global state.
  3. If it's a server-heavy application like this course platform, I use React + something to help with the network stuff. I'm using Vercel's SWR on this course platform and I have no complaints.

This is the formula I've found works best, after years of experience and dozens of projects, but a lot of this really does come down to subjective preference.

But here's something I can say pretty objectively: React has evolved tremendously over the past few years, and has become way more capable. You can absolutely build large, sophisticated applications without any other state-management libraries. You don't need Redux, and you shouldn't feel pressured to learn it.

If you're new to React, here's what I suggest: spend a year or two getting comfortable building apps with React, using the tools covered in this course. You can always explore Redux in the future, and see if you find it helpful!

Honestly, there aren't very many "wrong" ways to build apps. We live in an era of abundance, and there are lots of amazing tools. The most important thing is not to become overwhelmed by trying to learn everything at once!

All of that said, there's one more wrinkle we should talk about: Redux Toolkit.

Redux Toolkit and RTK-Query

Over the past few years, the Redux team has been hard at work creating Redux Toolkit, an opinionated toolset that aims to sand down some of the rough edges of working with Redux.

This toolkit includes a project, RTK Query, a tool which is more-or-less a Redux-compatible version of react-query / SWR.

I would be pretty excited about this development… but unfortunately, I have a fundamental disagreement with the opinions that are baked into Redux Toolkit 😅. I'll share my quibbles below for anyone curious, but the takeaway is that I plan on continuing to use "Classic" Redux, without Redux Toolkit.

If you're already using Redux Toolkit, I think RTK Query is a slam dunk. It makes way more sense to use something tightly-integrated, rather than trying to get Redux and SWR to play nicely together.

Otherwise, it's hard for me to say whether you should use this combination over something else. It wouldn't be my first choice, but as I said above, there are lots of great options nowadays, and this one might work best for you, depending on the type of app you're building and your subjective preferences.